package gbench.appdemo.mall.erp;

import static gbench.common.matlib.MatlibCanvas.println;
import static gbench.common.matlib.rdd.BasePair.bp;
import static gbench.common.tree.LittleTree.JOIN;
import static gbench.common.tree.LittleTree.kvp_int;
import static gbench.common.tree.LittleTree.trycatch;
import static gbench.common.tree.LittleTree.IRecord.REC;
import static gbench.common.tree.LittleTree.SQL.nsql;
import static gbench.common.tree.LittleTree.Term.FT;

import java.text.NumberFormat;
import java.time.LocalDateTime;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import gbench.common.matlib.MatlibCanvas.XVec;
import gbench.common.matlib.data.DataReader.DFrame;
import gbench.common.matlib.rdd.BasePair;
import gbench.common.tree.LittleTree.IRecord;
import gbench.common.tree.LittleTree.Jdbc;
import gbench.common.tree.LittleTree.SQL;
import gbench.common.tree.LittleTree.Term;
import gbench.appdemo.mall.erp.components.*;

/**
 * ERP App
 * @author gbench
 *
 */
public class MyERPApp {
    
    /**
     * 平台初始化
     * @param jdbc
     */
    public MyERPApp(final Jdbc jdbc){
        this.jdbc = jdbc;
        financeApp = new FinanceApp();
        dataApp = new MyDataApp(jdbc);
        this.initialize();
    }
    
    /**
     * 
     * @author gbench
     *
     * @param <S>
     */
    public class MyERPEntity <S> extends Entity<S> {
        
        /**
         * 
         * @return
         */
        public MyERPApp erpApp() {
            return MyERPApp.this;
        }
        
        /**
         * 
         * @return
         */
        public MyDataApp dataApp() {
            return this.erpApp().dataApp;
        }
        
        /**
         * getAttributes 的别名
         * @return
         */
        public IRecord attrs() {
            return this.getAttributes();
        }
    }

    /**
     * 行项目 <br>
     * 订单项目
     * 
     * @author Administrator
     *
     */
    public class LineItem implements ILineItem {
        /**
         * 行项目
         * 
         * @param product 产品对象
         * @param quantity 产品数量
         */
        public LineItem(final IProduct product, final Number quantity) {
            this.product = product;
            this.quantity = quantity.doubleValue();
            this.unitPrice = product.getPrice();
        }

        /**
         * 行项目 <br>
         * 
         * @param priceQuote 产品报价
         * @param quantity   产品数量
         */
        public LineItem(final IPriceQuote priceQuote, final Number quantity) {
            this.product = priceQuote.getProduct();
            this.quantity = quantity.doubleValue();
            this.unitPrice = priceQuote.getUnitPrice();
        }

        /**
         * 名称
         * 
         * @return 名称
         */
        @Override
        public String getName() {
            return FT("$0", this.product.getName());
        }

        /**
         * 价格总结
         * 
         * @return
         */
        @Override
        public IPrice getTotal() {
            return this.unitPrice.mul(this.quantity);
        }

        /**
         * @return the product
         */
        @Override
        public IProduct getProduct() {
            return product;
        }

        /**
         * 数量
         * 
         * @return the quantity
         */
        @Override
        public Double getQuantity() {
            return quantity;
        }

        /**
         * 单价
         * 
         * @return the unitPrice
         */
        public IPrice getUnitPrice() {
            return unitPrice;
        }

        /**
         * 税费
         * 
         * @return the tax
         */
        public IPrice getTax() {
            return tax;
        }

        /**
         * 格式化输出
         */
        public String toString() {
            return FT("$0\t$1\t$2", product.getName(), this.quantity, this.getTotal());
        }
        
        /**
         * hashCode
         */
        public int hashCode() {
            final var code = Objects.hash(product,quantity,unitPrice,tax);
            return code;
        }
        
        /**
         * 
         */
        public boolean equals(final Object obj) {
            if (!(obj instanceof LineItem)) return false;
            final var o = (LineItem) obj;
            
            return (product == o.product || product.equals(o.product))
                    && (quantity == o.quantity || quantity.equals(o.quantity))
                    && (unitPrice == o.unitPrice || unitPrice.equals(o.unitPrice))
                    && (tax == o.tax || tax.equals(o.tax));
        }

        private IProduct product;
        private Double quantity;
        private IPrice unitPrice;
        private IPrice tax;
    }
    
    /**
     * 产品列表
     * 
     * @author Administrator
     *
     */
    public class Product extends MyERPEntity<IProduct> implements IProduct {
        
        /**
         * 产品记录
         * @param rec 产品记录
         */
        public Product(final IRecord rec) {
            this(rec.i4("id"),rec.str("name"),rec.dbl("price"),rec.i4("money",1));
            this.setAttributes(rec);
        }

        /**
         * 基础构造函数
         * 
         * @param id 产品id
         * @param name 产品名称
         * @param price 产品价格
         * @param moneyId 产品计价货币
         */
        public Product(final Integer id, final String name, final Double price,final int moneyId) {
            this.id = id;
            this.name = name;
            this.price = price;
            this.money = financeApp.getMoneyById(moneyId);
        }
        
        /**
         * 
         * @return
         */
        public IPrice getPrice() {
            return financeApp.new  Price(this.price,money);
        }
        
        /**
         * 
         * @return
         */
        @Override
        public String getBrand() {
            return this.brand;
        }
        
        /**
         * 
         * @return
         */
        @Override
        public String getQuality() {
            return this.quality;
        }
        
        /**
         * hashCode
         */
        public int hashCode() {
            final var code = Objects.hash(price,money,brand,quality);
            return code;
        }
        
        /**
         * 等价关系
         */
        public boolean equals(Object obj) {
            if (!(obj instanceof Product))
                return false;
            final var o = (Product) obj;
            var b = false;

            b = this.price == o.price && (money == o.money || money.equals(o.money))
                    && (brand == o.brand || brand.equals(o.brand))
                    && (quality == o.quality || quality.equals(o.quality));

            return b;
        }

        private double price;
        private IMoney money;
        private String brand;
        private String quality;
    }

    /**
     * 报价单
     * 
     * @author Administrator
     *
     */
    public class PriceQuote implements IPriceQuote {
        
        /**
         * 
         * @param product
         * @param unitPrice
         */
        public PriceQuote(final IProduct product,final IPrice unitPrice){
            this.product = product;
            this.unitPrice = unitPrice;
        }
        
        /**
         * IRecord 格式来源于 ICatalog.getQuoteS
         * @param rec {id:productId,price:unitPrice,onwerid:ownerid,offerid:priceQuoteId}
         */
        public PriceQuote(final IRecord rec) {
            this.setAttributes(rec);
            final var productId = rec.i4("id");
            this.product = MyERPApp.this.productOf(productId);
            final var financeApp =MyERPApp.this.financeApp;
            final var price = rec.dbl("price");
            if(this.product!=null) {
                if(this.product.getPrice()!=null)
                    this.unitPrice = financeApp.price(price, this.product.getPrice().getMoney());
                else
                    this.unitPrice = financeApp.rmb(price);
            }
        }
        
        /**
         * 需要注意报价id对应offerid字段
         */
        public Integer getId() {
            return this.attributes.i4("offerid");
        }
        
        /**
         * 获取报价详情的属主
         */
        public IEnterprise getOwner() {
            final var ownerid = this.attributes.i4("ownerid");
            if (ownerid == null) {
                return null;
            } else {
                return MyERPApp.this.enterpriseOf(ownerid);
            }
        }

        /**
         * @return the product
         */
        public IProduct getProduct() {
            return product;
        }
        
        /**
         * @return the unitPrice
         */
        public IPrice getUnitPrice() {
            return unitPrice;
        }
        
        /**
         * @return the descrition
         */
        public String getDescrition() {
            return descrition;
        }
        
        /**
         * @return the version
         */
        public String getVersion() {
            return version;
        }
        
        /**
         * 
         */
        public String toString() {
            return FT("$0#$1 : $2", this.product.getId(), this.product.getName(), this.unitPrice);
        }
        
        /**
         * hashCode
         */
        public int hashCode() {
            return Objects.hash(product,unitPrice,descrition,version);
        }
        
        /**
         * 
         */
        public boolean equals(final Object obj) {
            if (!(obj instanceof PriceQuote))
                return false;
            final var o = (PriceQuote) obj;
            return (product == o.product || product.equals(o.product))
                    && (unitPrice == o.unitPrice || unitPrice.equals(o.unitPrice))
                    && (descrition == o.descrition || descrition.equals(o.descrition))
                    && (version == o.version || version.equals(o.version));
        }
        
        /**
         * getAttributes 别名
         * 
         * @return the attributes
         */
        public IRecord attrs() {
            return getAttributes();
        }

        /**
         * @return the attributes
         */
        public IRecord getAttributes() {
            return attributes;
        }

        /**
         * @param attributes the attributes to set
         */
        public PriceQuote setAttributes(final IRecord attributes) {
            this.attributes = attributes;
            return this;
        }
        
        @Override
        public String getName() {
            return this.toString();
        }

        @Override
        public <T> T attr(String name, T defaultValue) {
            return this.attributes.get(name,defaultValue);
        }
        
        private IProduct product;
        private IPrice unitPrice;
        private String descrition;
        private String version;
        private IRecord attributes = REC(); // 报价的属性信息
        
    }

    /**
     * 状态对象
     * @author gbench
     *
     */
    public class Status extends MyERPEntity<Status> implements IStatus {
        
        /**
         * 状态对象
         * @param id 状态id
         */
        public Status(final Integer id) {
            this.id = id;
            switch (id) {
            case 0:
                this.name = "创建";
                break;
            case 1:
                this.name = "已审核";
                break;
            case 2:
                this.name = "待执行";
                break;
            case 3:
                this.name = "已完成";
                break;
            case 4:
                this.name = "已结算";
                break;
            default:
                this.name = "";
            }
        }
    }

    /**
     * 库存单
     * 
     * @author Administrator
     *
     */
    public class StoreBill<S> implements IStoreBill<S> {

        /**
         * 
         * @param order
         */
        @SuppressWarnings("unchecked")
        public <T> StoreBill(final IOrder<T> order) {
            this.order = (IOrder<Object>)order;
            this.lineItems = order.getLineItems(true);
        }

        /**
         * 
         * @param order
         */
        @SuppressWarnings("unchecked")
        public <T> StoreBill(final IOrder<T> order, final List<ILineItem> lineItems) {
            this.order = (IOrder<Object>)order;
            this.lineItems = lineItems;
        }

        /**
         * 仓储单
         * 
         * @param ratios 发货/收货比率
         * @return 仓储单
         */
        public <T,U> U map(final Function<IStoreBill<T>, U> mapper, final Double... ratios) {
            final var n = ratios.length;
            final List<ILineItem> lineItems = this.lineItems.stream().map(kvp_int()).map(p -> {
                final var originItem = p._2();
                final var qty = originItem.getQuantity() * ratios[p._1() % n];
                final var item = new LineItem(originItem.getProduct(), qty);
                return item;
            }).collect(Collectors.toList());
            final var sb = new StoreBill<T>(this.order, lineItems);
            return mapper.apply(sb);
        }

        /**
         * 
         * @param base
         * @return
         */
        public <T> XVec<BasePair<ILineItem, ILineItem>> baseline(final Order<T> base) {
            return XVec.OF(base.lineItems).zip(this.lineItems, BasePair::bp);
        }

        /**
         *
         * @return
         */
        public XVec<BasePair<ILineItem, ILineItem>> baseline() {
            return XVec.OF(this.order.getLineItems()).zip(this.lineItems, BasePair::bp);
        }

        /**
         * 
         * @return
         */
        public List<ILineItem> getLineItems() {
            return this.lineItems;
        }

        /**
         * 返回订单对象
         * 
         * @return
         */
        public IOrder<Object> getOrder() {
            return this.order;
        }
        
        /**
         * 
         * @param owner
         * @return
         */
        @SuppressWarnings("unchecked")
        public S setOwner(final IEnterprise owner) {
            this.owner = owner;
            return (S) this;
        }

        /**
         * 
         * @return
         */
        public IEnterprise getOwner() {
            return this.owner;
        };

        /**
         * 
         */
        public String toString() {
            return this.order.toString();
        }

        private IOrder<Object> order;
        private List<ILineItem> lineItems;
        private IEnterprise owner;
    }

    /**
     * 
     * @author Administrator
     *
     */
    public class InboundBill extends StoreBill<InboundBill> implements IInboundBill {

        public InboundBill(final IStoreBill<?> bill) {
            super(bill.getOrder(), bill.getLineItems());
            this.setOwner(bill.getOwner());
        }

    }

    /**
     * 
     * @author Administrator
     *
     */
    public class OutboundBill  extends StoreBill<OutboundBill> implements IOutboundBill {
    
        public OutboundBill(final IStoreBill<?> bill) {
            super(bill.getOrder(),bill.getLineItems());
            this.setOwner(bill.getOwner());
        }
        
    }

    /**
     * 发票
     * 
     * @author Administrator
     *
     */
    public class Invoice {
        /**
         * @return the lineItems
         */
        public List<LineItem> getLineItems() {
            return lineItems;
        }

        /**
         * @param lineItems the lineItems to set
         */
        public void setLineItems(List<LineItem> lineItems) {
            this.lineItems = lineItems;
        }
        
        private List<LineItem> lineItems;
    }

    /**
     * 产品目录
     * @author gbench
     *
     */
    public class Catalog extends MyERPEntity<Catalog> implements Iterable<IPriceQuote>, ICatalog {
        
        /**
         * 产品目录
         * 
         * @param rec(id:id,name:名称)
         */
        public Catalog(final IRecord rec) {
            this(rec.i4("id"), rec.str("name"));
        }

        /**
         * 基本构造函数
         * 
         * @param id   id
         * @param name 名称
         */
        public Catalog(final Integer id, final String name) {
            this.id = id;
            this.name = name;
        }
        
        @Override
        public Iterator<IPriceQuote> iterator() {
            return this.getPriceQuotes().iterator();
        }
        
        /**
         * 返回产品价格流
         * 
         * @return 产品价格流
         */
        public Stream<IPriceQuote> stream() {
            return this.getPriceQuotes().stream();
        }
        
        /**
         * 返回产品价格流
         * 
         * @param <T>    结果元素类型
         * @param mapper 元素变化函数 priceQuote->t
         * @return 产品价格流
         */
        public <T> Stream<T> stream(final Function<IPriceQuote, T> mapper) {
            return this.getPriceQuotes().stream().map(mapper);
        }
        
        /**
         * catalog 的产品变换
         * 
         * @param <U> 结果类型
         * @param collector 归集器
         * @return U 产品表的归集项目
         */
        public <U> U collect(Collector<? super IPriceQuote, ?, U> collector) {
            return this.stream().collect(collector);
        }
    }

    /**
     * 企业：市场主体
     * 
     * @author Administrator
     *
     */
    public class Enterprise extends MyERPEntity<IEnterprise> implements IEnterprise {
        
        public Enterprise(IRecord rec) {
            this(rec.i4("id"),rec.str("name"));
        }

        /**
         * 基本构造函数
         * @param id
         * @param name
         */
        public Enterprise(Integer id,String name) {
            this.id = id;
            this.name = name;
            this.store = new Store(Enterprise.this);
        }

        public void setStore(IStore store) {
            this.store = store;
        }
        
        public IStore getStore() {
            return this.store;
        }
        
        
        /**
         * 获取默认产品
         * 
         * @return 产品目录
         */
        public ICatalog getDefaultCatalog() {
            if(this.defaultCatalog==null) {
                final var catalogs = this.getCatalogs();
                if(catalogs.size()>0) { // 设定第一号的catalog为默认产品目录
                    this.defaultCatalog = catalogs.get(0);
                }// if
            }// if
            return this.defaultCatalog;
        }
        
        /**
         * 设置 产品目录
         * @param catalog 产品目录
         * @return 企业对象本身
         */
        public IEnterprise setDefaultCatalog(final ICatalog catalog) {
            this.defaultCatalog = catalog;
            return this;
        }
        
        /**
         * 获得库房
         * @return
         */
        public List<IWareHouse> getHouses(){
            return this.store.getHouses();
        }
        
        /**
         * @return the address
         */
        public String getAddress() {
            return address;
        }

        /**
         * @return the telephone
         */
        public String getTelephone() {
            return telephone;
        }

        /**
         * @param address the address to set
         */
        public void setAddress(String address) {
            this.address = address;
        }

        /**
         * @param telephone the telephone to set
         */
        public void setTelephone(String telephone) {
            this.telephone = telephone;
        }

        private String telephone;
        private String address;
        IStore store;
        private ICatalog defaultCatalog;
    }
    
    /**
     * 
     * @author gbench
     *
     * @param <T> 实体类型
     */
    public class FinanceEntity<T> extends MyERPEntity<T> implements IFinanceEntity<T> {
        
        /**
         * 金融主体
         * @param entity
         */
        public FinanceEntity(final Entity<T> entity){
            this.id = entity.getId();
            this.name = entity.getName();
            this.attributes = entity.getAttributes();
        }
        
        /**
         * 会计报表
         * @return DFrame
         */
        @Override
        public DFrame trialBalance() {
            final var flds = "entity_id transcode,drcr,item_id acctid,optional,description acct,if(drcr=0,1,-1)*amount total";
            final var s1 = FT("select $0 from t_entity_item_rel where name='t_transaction/t_coa' and ref='$1'",flds,this.getId());
            final var s2 = FT("select acctid , acct, optional, sum(total) balance, count(*) cnt from ($0) t1 group by acctid,optional ",s1); // acctid(科目)-optional(子科目) 的分类统计
            final var s3 = FT("select if(t2.balance>=0,'借','贷') drcr,t2.* from ($0) t2 order by drcr",s2);
            // println(s2);
            return dataApp.sqlqueryS(s3).collect(DFrame.dfmclc);
        }
        
        /**
         * 事务函数
         * trans 交易名称
         * @param recs 会计记账信息 (name,create_time,entries:[(drcr,acctid,amount)]
         */
        @Override
        public void withAcctTransaction(final IRecord ... recs) {
            @SuppressWarnings("unchecked")
            final BasePair<IFinanceEntity<Enterprise>,IRecord>[] acct_txs = Stream.of(recs).map(e->bp(this, e)).toArray(BasePair[]::new);
            MyERPApp.this.withAcctTransaction(acct_txs);
        }
    }
    
    /**
     * 银行 任何企业 都是一个银行对象<br>
     * 
     * 银行就是 一个 拥有记账功能的 财务实体。
     * 一个银行对象 就是一个 拥有采用记账功能的企业, 没有什么特殊性 <br>
     * 一般我们会把一个企业 转换成一个 银行对象 来对该企业进行 财务核算。
     * @author gbench
     *
     */
    public class Bank extends FinanceEntity<IEnterprise> implements IBank {

        /**
         *
         * @param entity
         */
        public Bank(final Entity<IEnterprise> entity) {
            super(entity);
        }
        
        /**
         * 
         * @param enterprise
         */
        @SuppressWarnings("unchecked")
        public Bank(final IEnterprise enterprise) {
            super((Entity<IEnterprise>) (Object) enterprise);
        }
        
        /**
         * 存款
         * dr 库存现金
         * cr 吸收存款
         */
        public <T> void deposit(final IFinanceEntity<T> entity) {
            
        }
        
        /**
         * 取款：
         * dr 吸收存款
         * cr 库存现金
         */
        public <T> void withdraw(final IFinanceEntity<T>  entity) {
            
        }
        
    }

    /**
     * Store 是一个House的分组
     * 
     * @author Administrator
     *
     */
    public class Store extends MyERPEntity<Store> implements IStore {
    
        Store(Enterprise owner) {
            owner.setStore(this);
            this.owner = owner;
            this.name = this.owner.getName();
        }
        
        /**
         * 
         */
        public IWareHouse createHouse(final IRecord rec) {
            return this.new WareHouse(rec);
        }
        
        /**
         * 初始化仓库
         */
        public void initialize() {
            final var items = dataApp.relqueryR("t_fortune", "t_house", this.owner.getId());
            final Store store = (Store) owner.getStore();
            items.forEach(r -> {
                dataApp.sql2maybe(FT("select * from t_house where id=$0",r.str("item_id")))
                .map(rec->{
                    final var house = store.new WareHouse(rec);
                    store.addWareHouse(house);
                    return house;
                });
            }); // forEach
        }
    
        /**
         * 添加库房
         * 
         * @param house
         */
        public void addWareHouse(IWareHouse house) {
            this.houses.add(house);
        }
    
        /**
         * 库房
         * 
         * @author Administrator
         *
         */
        class WareHouse extends MyERPEntity<IWareHouse> implements IWareHouse{

            /**
             *
             * @param rec
             */
            WareHouse(final IRecord rec){
                this(rec.i4("id"),rec.str("name"),rec.str("address"));
                //println(rec);
            }
    
            /**
             * 
             * @param name
             */
            WareHouse(final Integer id, String name,String address) {
                this.id = id;
                this.name = name;
                this.address = address;
            }
            
            
            
            /**
             * 获取地址
             * @return
             */
            public String getAddress() {
                return this.address;
            }
            
            private String address;
        }
    
        /**
         * 存货,存货是 绑定了产品的与库房的产品。
         * 
         * @author Administrator
         *
         */
        class Stock {
            Product product;
            WareHouse house;
            Double quantity;
        }
    
        /**
         * 
         * @return
         */
        public IWareHouse getDefaultWareHouse() {
            if (this.defaultWareHouse == null && this.houses.size() > 0) {
                this.defaultWareHouse = this.houses.get(0);
            }
        
            return this.defaultWareHouse;
        }

        /**
         * 默认库房
         * @param house
         */
        public void setDefaultHouse(IWareHouse house) {
            this.defaultWareHouse = house;
        }
        
        /**
         * 添加房屋
         * @param house
         */
        public void addHouse(final IWareHouse house) {
            final var entityTbl = "t_fortune";
            final var itemTbl = "t_house";
            println(house);
            final var opt = dataApp.rel2maybe_rec_f(entityTbl, itemTbl).apply(REC("id", house.getId()));
            if (opt.isEmpty()) {
                final var rec = REC("item_id", house.getId(), "entity_id", this.owner.getId(), "optional", this.owner.getName()+"-库房-"+house.getName(), "drcr", -1,"flag",0,
                        "quantity", -1, "amount", -1, "description", house.getName(), "create_time", LocalDateTime.now());
                dataApp.rel2insert_rec_f(entityTbl, itemTbl).apply(rec);
            } else {
                println("house 已经存在", house, opt);
            }
        }
        
        @Override
        public List<IWareHouse> getHouses() {
            return houses;
        }

        /**
         * 
         * @return
         */
        public IEnterprise getOwner() {
            return this.owner;
        }
    
        private IEnterprise owner;
        private IWareHouse defaultWareHouse;
        private List<IWareHouse> houses = new LinkedList<IWareHouse>();
    }

    /**
     * 合约对象<br>
     * 一个合约对象 表示这是一个 双方就谋事 而达成的 一种约定,是一种约定关系
     * 
     * @author gbench
     *
     */
    public class Contract extends MyERPEntity<IContract> implements IContract {
        
        /**
         * 默认构造
         */
        protected Contract() {

        }

        /**
         * 合约对象
         * 
         * @param partA 甲方
         * @param partB 乙方
         */
        public Contract(final IEnterprise partA, final IEnterprise partB) {
            this(null, partA, partB);
        }

        /**
         * 合约对象
         * 
         * @param id    合同id
         * @param partA 甲方
         * @param partB 乙方
         */
        public Contract(final Integer id, final IEnterprise partA, final IEnterprise partB) {
            this.id = id;
            this.partA = partA;
            this.partB = partB;
            this.status = new Status(0);
        }
        
        /**
         * 数据格式化
         */
        public String toString() {
            return FT("#$0-$1-$2", this.id == null ? "NEW" : this.id, this.name, this.createTime);
        }

        /**
         * @return the partA
         */
        public IEnterprise getPartA() {
            return partA;
        }

        /**
         * @return the partB
         */
        public IEnterprise getPartB() {
            return partB;
        }

        /**
         * @return the status
         */
        public IStatus getStatus() {
            return status;
        }

        /**
         * @return the createTime
         */
        public LocalDateTime getCreateTime() {
            return createTime;
        }

        /**
         * @param partA the partA to set
         */
        public void setPartA(Enterprise partA) {
            this.partA = partA;
        }

        /**
         * @param partB the partB to set
         */
        public void setPartB(Enterprise partB) {
            this.partB = partB;
        }

        /**
         * @param status the status to set
         */
        public void setStatus(Status status) {
            this.status = status;
        }

        /**
         * @param createTime the createTime to set
         */
        public void setCreateTime(LocalDateTime createTime) {
            this.createTime = createTime;
        }
        
        /**
         * 甲方
         */
        private IEnterprise partA;

        /**
         * 乙方
         */
        private IEnterprise partB;

        /**
         * 合同状态
         */
        private Status status = new Status(0);

        /**
         * 创建时间
         */
        private LocalDateTime createTime = LocalDateTime.now();
    }

    /**
     * 订单对象
     * 
     * @author Administrator
     *
     */
    public class Order<S> extends Contract implements Iterable<ILineItem>, IOrder<S>{
        
        /**
         * 订单对象
         * 
         * @param contract 合同对象
         */
        public Order(final IContract contract) {
            super(contract.getId(), contract.getPartA(), contract.getPartB());
            this.name = contract.getName();
        }
        
        /**
         * 
         * @param partA
         * @param partB
         */
        public Order(final IEnterprise partA, final IEnterprise partB) {
            super(partA, partB);
        }

        

        /**
         * 
         * @param product 产品对象
         * @param n 产品数量
         */
        public void addItem(final Product product, final Number n) {
            final var item = new LineItem(product, n.doubleValue());
            this.lineItems.add(item);
        }
        
        /**
         * 加入所有lineItems
         * @param lineItems
         */
        public void addAll(final List<ILineItem> lineItems) {
            this.lineItems.addAll(lineItems);
        }
        
        /**
         * 
         * @param id
         * @return 设置后的id
         */
        @Override
        public Integer setId(Integer id) {
            this.id = id;
            return this.id;
            
        }

        /**
         *
         * @param ll
         * @param <T>
         * @return
         */
        @SuppressWarnings("unchecked")
        public <T extends ILineItem> Integer setLineItems(final List<T> ll) {
            this.lineItems = (List<ILineItem>)ll;
            return this.id;
            
        }
        
        /**
         * 行项目
         * 
         * @return 行项目列表
         */
        public List<ILineItem> getLineItems() {
            return this.lineItems.size() > 0 ? this.lineItems : this.getLineItems(true);
        }
        
        /**
         * 订单总额
         * 
         * @return 获取总额
         */
        public Double getTotal() {
            return this.stream().collect(Collectors.summarizingDouble(e->e.getUnitPrice().getAmount() * e.getQuantity())).getSum();
        }
        
        /**
         * 订单的刷新
         */
        @SuppressWarnings("unchecked")
        public S refresh() {
            this.lineItems.clear();
            this.lineItems.addAll(this.getLineItems(true));
            return (S) this;
        }
        
        /**
         * 迭代器
         */
        @Override
        public Iterator<ILineItem> iterator() {
            return lineItems.iterator();
        }

        /**
         *
         * @param action
         */
        @Override
        public void forEach(Consumer<? super ILineItem> action) {
            this.getLineItems().forEach(action);
        }

        /**
         * 数据流
         * @return LineItem 的流
         */
        public Stream<ILineItem> stream(){
            return StreamSupport.stream(this.spliterator(), false);
        }

        /**
         * 订单的格式化输出
         */
        public String toString() {
            if (this.lineItems.size() < 1) this.refresh(); // 刷新行项目
            final var details = this.lineItems.stream().map(e->e.getProduct().getName()+"/"+e.getQuantity()).collect(Collectors.joining(","));
            return FT("<$0[$1]>",super.toString(),details.length()>50?details.substring(0,50)+"...":details);
        }

        private List<ILineItem> lineItems = new LinkedList<ILineItem>();

    }

    /**
     * 采购订单
     * 
     * @author gbench
     *
     */
    public class PurchaseOrder extends Order<IPurchaseOrder> implements IPurchaseOrder {
        
        /**
         * 采购订单
         * 
         * @param order 订单
         */
        public PurchaseOrder(final IOrder<?> order) {
            super(order);
        }

        /**
         * 基本构造函数
         * 
         * @param partA 甲方
         * @param partB 乙方
         */
        public PurchaseOrder(final IEnterprise partA, final IEnterprise partB) {
            super(partA, partB);
            this.name = FT("PO-$0-$1", partA.getId(), partB.getId());
        }

        /**
         * 
         * @return
         */
        public IEnterprise getSupplier() {
            return this.getPartB();
        }

        /**
         * 获取订单属主
         * 
         * @return 订单属主
         */
        public IEnterprise getOwner() {
            return this.getPartA();
        }

    }

    /**
     * 销售订单
     * @author gbench
     *
     */
    public class SaleOrder extends Order<ISaleOrder> implements ISaleOrder {
        
        public SaleOrder(final IOrder<?> order) {
            super(order);
        }

        /**
         * 基本构造函数
         * @param partA
         * @param partB
         */
        public SaleOrder(IEnterprise partA, IEnterprise partB) {
            super(partA, partB);
            this.name = FT("SO-$0-$1",partA.getId(),partB.getId());
        }
        
        /**
         * 
         * @return
         */
        public IEnterprise getCustomer() {
           return this.getPartA();
        }
        
        /**
         * 
         * @return
         */
        public IEnterprise getOwner() {
            return this.getPartB();
        }
    }

    /**
     * 结算单
     * 
     * @author Administrator
     *
     */
    public class Settlement {

    }
    
    /**
     * MyERPApp 的初始化
     */
    public void initialize() {
       financeApp.initialize(dataApp.sqlqueryS("select * from t_money where provider=0"));// 补充货币信息
    }

    /**
     * 企业id
     * @param id 企业id
     * @return Enterprise
     */
    public IEnterprise getEnterprise(final int id) {
        return this.getEnterprise(id,false);
    }

    /**
     * 获取市场主体
     * 
     * @param id 企业id
     * @return Enterprise
     */
    public IEnterprise getEnterprise(final int id, final boolean b) {
        return dataApp.sql2maybe(FT("select * from t_fortune where id = $0", id))
                .map(e -> !b ? new Enterprise(e) : new Enterprise(e).initialize()).orElse(null);
    }
    
    /**
     * 获取一个 金融主体
     * @param enterprise 企业主体
     * @return Bank 银行
     */
    public FinanceEntity<IEnterprise> getBank(final Enterprise enterprise) {
       return new FinanceEntity<IEnterprise>(enterprise);
    }
    
    /**
     * 获取一个 金融主体
     * @param id 企业id
     * @return Bank 银行
     */
    public Bank getBank(final int id, final boolean b) {
        return new Bank(this.getEnterprise(id, b));
    }
    
    /**
     * 获得银行对象：可以进行会计记账,任何企业 都是一个银行对象
     * @param id 企业id
     * @return Bank
     */
    public Bank getBank(final int id) {
        return new Bank(this.getEnterprise(id, false));
    }

    /**
     * 加载订单
     * @param id 订单id
     * @return Order订单对象
     */
    public IOrder<?> getOrder(final int id){
        final var sql = FT("select * from t_contract where id=$0",id);
        //println(sql);
        final var contract = dataApp.sql2maybe(sql).map(e -> {
            final var partA = MyERPApp.this.getEnterprise(e.i4("partA"));
            final var partB = MyERPApp.this.getEnterprise(e.i4("partB"));
            //println(e);
            final var c = new Contract(e.i4("id"),partA,partB);
            c.createTime = e.ldt("create_time");
            c.name = e.str("name");
            return c;
        }).orElse(null);
        
        final var order = new Order<>(contract);
        //println("order",id,order);
        return order;
    }

    /**
     * 获得产品流
     * 
     * @return [Product]
     */
    public Stream<Product> getProductS() {
        return dataApp.sqlqueryS("select * from t_product").map(Product::new);
    }

    /**
     * 获取货币
     * 
     * @param moneyId 货币id
     * @return 货币对象
     */
    public IMoney getMoney(int moneyId) {
        return this.financeApp.getMoneyById(moneyId);
    }
    
    /**
     * 产品对象
     * @param name 产品名
     * @return 产品对象
     */
    public IProduct productOf(String name) {
        return dataApp.sql2maybe(FT("select * from t_product where name='$0'",name)).map(Product::new).orElse(null);
    }
    
    /**
     * 产品对象
     * @return 产品对象
     */
    public IProduct productOf(IRecord rec) {
        return new Product(rec);
    }
    
    /**
     * 产品对象
     * @param productId 产品Id
     * @return 产品对象
     */
    public IProduct productOf(Integer productId) {
        final var sql = SQL.of("t_product", REC("id",productId)).select();
        return dataApp.sql2maybe(sql).map(this::productOf).orElse(null);
    }
    
    /**
     * 产品对象
     * @param enterpriseId 产品Id
     * @return 产品对象
     */
    public IEnterprise enterpriseOf(Integer enterpriseId) {
        return this.getEnterprise(enterpriseId);
    }

    /**
     * 数据模块
     * 
     * @return
     */
    public MyDataApp dataApp() {
        return this.dataApp;
    }

    /**
     * 金融模块
     * 
     * @return
     */
    public FinanceApp financeApp() {
        return this.financeApp;
    }
    
    /**
     * 会计交易
     * trans 交易名称
     *
     * @param acct_txs
     * @param <T>
     */
    @SafeVarargs
    public final <T extends IFinanceEntity<?>> void withAcctTransaction(final BasePair<T,IRecord> ... acct_txs) {
        this.withAcctTransaction((MyDataApp) null, acct_txs);
    }
    
    /**
     * 会计交易的处理
     * 
     * @param acct_txs 会计交易 集合
     */
    @SafeVarargs
    public final <T extends IFinanceEntity<?>> void withAcctTransaction(MyDataApp dataApp,final BasePair<T,IRecord> ... acct_txs) {
        
        // 开启数据会话
        ( dataApp==null ? this.dataApp : dataApp ).withTransaction(sess -> {
            for ( final var acct_tx : acct_txs ) {
                final var finance_entity = acct_tx._1(); // 提取金融实体
                final var rec = acct_tx._2(); // 提取 交易交易
                
                // 账套编号(会计主体id) 一般用财务实体编号 给予 表示 , 比如 银行的id。 
                // 分录( t_entity_item_rel 的 row) 通过 ref(ownerid) 来 记录 ownerid, 这样 我们
                // 在以后 查询的时候 就可以 通过 ref来提取 属于 该 financeEntity 的 复式记账分录列表。
                final var ownerid = finance_entity.getId();  // 会计主体id
                
                // 生成会计交易
                final var trans = REC("name",rec.str("name"),"create_time",LocalDateTime.now()); // 会计交易
                final int transid = sess.sql2execute2int(SQL.of("t_transaction", trans).insert()); // 会计交易 id
                
                // 分录明细
                final var createTime = rec.get("create_time",LocalDateTime.now()); // 创建时间
                final var entries = rec.lla("entries", IRecord.class); // 提取记账分录
                final var items = new LinkedList<IRecord>(); // 会计分录
                final var fracf = NumberFormat.getInstance(); // 小数部分 子科目编号 0 表示默认子科目
                final var intf = NumberFormat.getInstance(); // 整数部分 主科目编号
                fracf.setMaximumIntegerDigits(0); fracf.setMinimumFractionDigits(1); fracf.setGroupingUsed(false); // 小数部分 
                intf.setMaximumFractionDigits(0); intf.setGroupingUsed(false); // 整数部分
                
                for(final var entry : entries) {
                    
                    // 分析账户结构
                    final var _acctid = entry.dbl("acctid"); // 账户编码
                    final var acctid = intf.format(_acctid); // 提取账户编码
                    final var subacctid = Optional.of(fracf.format(_acctid)).map(e->e.matches("\\s*")?".0":e).map(e->e.substring(1)).get(); // 子科目id
                    final var acct_sql0 = FT("select name from t_coa where code ='$0.$1'", acctid,subacctid); // 提取 账户名称 - 带有 子科目
                    final var acct_sql1 = FT("select name from t_coa where code ='$0.0'", acctid); // 提取 账户名称 - 不带有子科目
                    final var acct = entry.get("acct",sess.sql2maybe(acct_sql0) // 带有子科目的 账户检索
                        .or(()->trycatch((String sql)->sess.sql2maybe(sql)).apply(acct_sql1)) // 舍弃子科目账户id重新检索 即 只检索 主科目id
                        .map(e -> e.str("name")).orElse("-")); // 获取 账户名称
                    
                    // 提取分录明细
                    final var drcr = entry.get("drcr"); // 科目的借贷方向
                    final var money = entry.get("money", 1); // 货币代码 默认为 人民币
                    final var amount = entry.get("amount"); // 账户的金额
                    final var description = FT("$0",entry.get("name", acct)); // 科目名称
                    final var path = "D:/sliced/files/products/valuables/gold.jpg"; // 默认图片路径
                    
                    // entity_id:交易代码, item_id:账户id, optional:子科目账户, quantity:货币代码, ref: 会计主体id
                    items.add(REC("path", path, "drcr", drcr, "name", "t_transaction/t_coa", "entity_id", transid, "item_id", acctid, 
                        "optional", subacctid , "amount", amount, "flag", 0, "quantity", money, "ref", ownerid, // ref 用于标识 该分录具体输入 哪个账套 如 财务(银行)实体
                        "description", description,"create_time",createTime));
                    // println(entry);
                } // for
                
                final var flds = items.get(0).keys();
                final var tpl = FT("insert into t_entity_item_rel (flds$) values ${foreach item in items (values$)}",
                    REC("flds$", JOIN(flds, ","), "values$", JOIN(flds.stream(),i -> "item." + i, ","))); // 明细项目的插入模板
                final var sql = nsql(tpl, REC("items", items));
                
                //println(sql.string2());
                sess.sql2execute2int(sql); // 存入数据
            }// for recs
        }); // withTransaction
    } // withTransaction
    
    /**
     * 生成 行项目
     * 
     * @param n 数量
     * @return LineItem 生成器
     */
    public Function<PriceQuote, LineItem> priceQuote2lineItem(final int n) {
        return priceQuote -> new LineItem(priceQuote, n);
    }
    
    /**
     * 生成 行项目
     * 
     * @param <T> 数量类型
     * @return LineItem 生成器 (priceQuote,quantiy)->LineItem
     */
    public <T extends Number> Function<BasePair<IPriceQuote, T>, ILineItem> priceQuote2lineItem() {
        return bp -> new LineItem(bp._1(), (Number) bp._2());
    }
    
    /**
     * withTransaction 函数
     * 
     * @param <T>    结果类型
     * @param mapper data->T 结果映射
     * @return T
     */
    public <T> T withDataTransaction(final Function<MyDataApp, T> mapper) {

        return mapper.apply(dataApp);
    }
    
    /**
     * 
     * @param sql
     * @return
     */
    public Stream<IRecord> sqlqueryS(String sql){
        return this.dataApp.sqlqueryS(sql);
    }
    
    /**
     * 
     * @param sql
     * @return
     */
    public Optional<IRecord> sql2maybe(String sql){
        return this.dataApp.sql2maybe(sql);
    }
    
    /**
     * 
     * @param <T>
     * @param tclass
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> Function<IRecord,T> ctor(final Class<T> tclass){
        return rec->{
            if (tclass == Catalog.class || tclass == ICatalog.class) return (T) new Catalog(rec);
            if (tclass == Product.class || tclass == IProduct.class) return (T) new Product(rec);
            if (tclass == Enterprise.class || tclass == IEnterprise.class) return (T) new Enterprise(rec);
            
            return null;
        };
    }
    
    /**
     * 
     * @param product
     * @param unitPrice
     */
    public PriceQuote priceQuoteOf(final IProduct product, final IPrice unitPrice) {
        return new PriceQuote(product, unitPrice);
    }
    
    /**
     * 
     * @param rec
     */
    public PriceQuote priceQuoteOf(final IRecord rec) {
        return new PriceQuote(rec);
    }
    
    /**
     * 根据报价id提取报价信息
     */
    public IPriceQuote priceQuoteOf(Integer priceQuoteId) {

        final var sql = Term.FT( "select entity_id id, quantity price, id offerid, drcr ownerid, description from t_entity_item_rel where id=$0", priceQuoteId);
        final var priceQuote = dataApp.sql2maybe(sql).map(this::priceQuoteOf).orElse(null);
        return priceQuote;
    }
    
    /**
     * 
     * @param order
     * @return
     */
    public ISaleOrder saleOrderOf(final IOrder<?> order) {
        return new SaleOrder(order);
    }
    
    /**
     * 基本构造函数
     * @param partA
     * @param partB
     */
    public ISaleOrder saleOrderOf(IEnterprise partA, IEnterprise partB) {
        return new SaleOrder(partA,partB);
    }
    
    /**
     * 
     * @param order
     * @return
     */
    public IPurchaseOrder purchaseOrderOf(final IOrder<?> order) {
        return new PurchaseOrder(order);
    }
    
    /**
     * 基本构造函数
     * 
     * @param partA 甲方
     * @param partB 乙方
     */
    public IPurchaseOrder purchaseOrderOf(final IEnterprise partA, final IEnterprise partB) {
        return new PurchaseOrder(partA,partB);
    }
    
    
    /**
     * 行项目
     * 
     * @param product 产品对象
     * @param quantity 产品数量
     */
    public ILineItem lineItemOf(final IProduct product, final Number quantity) {
        return new LineItem(product,quantity);
    }
    
    protected final FinanceApp financeApp;
    protected final MyDataApp dataApp;
    protected final Jdbc jdbc;
}